#include "Sound.h"

#include <SDL_audio.h>
#include <SDL_mixer.h>
#include <iostream>
#include <stdio.h>
#include <math.h>

void PrintSFInfo(const struct SF_INFO& myInfo)
{
	std::cout << "Frames: " << (int)myInfo.frames << std::endl;
	std::cout << "Channels: " << myInfo.channels << std::endl;
	std::cout << "SampleRate: " << myInfo.samplerate << std::endl;
	std::cout << "Format: " << myInfo.format << std::endl;
	std::cout << std::endl;
}

Sound::Sound()
{
	m_volume = 1.0f;
	m_normalised = false;
	m_reversed = false;
	m_totalNumOfFrames = 0;
	m_numOfLoops = 0;
	m_wasChanged = false;
	m_maxVal = 0.0f;
	m_lpFilter = 0.0f;
	m_fadeIn = 0.0f;
	m_fadeOut = 0.0f;
	m_minFreq = 0.0f;
	m_maxFreq = 1.0f;
	m_frequency = 1.0f;
	m_origFreq = 0.0f;
}

bool Sound::LoadSound(const std::string& filename)
{
	if (!(m_fileHandle = sf_open(filename.c_str(), SFM_READ, &m_fileInfo)))
	{
		std::cout << "Failed to open file" << std::endl;
		puts(sf_strerror(NULL));
		return false;
	}

	m_totalNumOfFrames = int(m_fileInfo.frames * m_fileInfo.channels);

	// init the buffers
	m_dataOrig = (float*) malloc(m_totalNumOfFrames * sizeof(float) * m_fileInfo.channels);
	memset(m_dataOrig, 0, m_totalNumOfFrames * sizeof(float));

	m_dataMod = (float*) malloc(m_totalNumOfFrames * sizeof(float) * m_fileInfo.channels);
	memset(m_dataMod, 0, m_totalNumOfFrames * sizeof(float));

	m_dataReversed = (float*) malloc(m_totalNumOfFrames * sizeof(float) * m_fileInfo.channels);
	memset(m_dataReversed, 0, m_totalNumOfFrames * sizeof(float));

	sf_read_float(m_fileHandle, m_dataOrig, m_totalNumOfFrames);
	SF_INFO temp = m_fileInfo;
	
	for (int i = 0; i != m_totalNumOfFrames; i++)
	{
		float cur = m_dataOrig[i];
		cur = abs(cur);

		if (cur > m_maxVal)
		{
			m_maxVal = cur;
		}
	}

	// set audio settings
	PrintSFInfo(m_fileInfo);
	m_origFreq = m_fileInfo.samplerate;

	if (Mix_OpenAudio(m_fileInfo.samplerate, MIX_DEFAULT_FORMAT, (int)m_fileInfo.channels, 512) != 0)
	{
		std::cout << "Failed to open audio: " << Mix_GetError() << std::endl;
		return false;
	}
	// create buffer file
	m_fileHandle = sf_open("../../Assets/buffer.wav", SFM_WRITE, &m_fileInfo);
	sf_write_float(m_fileHandle, m_dataOrig, m_totalNumOfFrames);

	m_fileInfo = temp;
	sf_close(m_fileHandle);

	m_sound = Mix_LoadWAV("../../Assets/buffer.wav");
	//m_sound =  Mix_LoadWAV_RW(SDL_RWFromFile("buffer.wav", "rb"), 1);
	if (m_sound == NULL)
	{
		std::cout << "Failed to load sound: " << Mix_GetError() << std::endl;
		return false;
	}

	CalculateSignal("../../Assets/buffer.wav");
	Reload();
	return true;
}

void Sound::SaveSound(const std::string& s)
{
	// save sound
	CalculateSignal(s);
}

void Sound::Reload()
{
	Mix_FreeChunk(m_sound);

	PrintSFInfo(m_fileInfo);

	//Mix_CloseAudio();
	//if (Mix_OpenAudio(m_fileInfo.samplerate * m_maxFreq, MIX_DEFAULT_FORMAT, (int)m_fileInfo.channels, 512) != 0)
	//{
	//	std::cout << "Failed to open audio: " << Mix_GetError() << std::endl;
	//	return;
	//}

	m_sound =  Mix_LoadWAV("../../Assets/buffer.wav");
	if (m_sound == NULL)
	{
		std::cout << "Failed to load sound: " << Mix_GetError() << std::endl;
		return;
	}
}

// lp filter = averaging out previous samples
// (xn-1 + xn-2 + xn-3 + xn-4) / 4 [4 because of num of samples taken]
// more samples = smoother curves
// less samples = sharper curves
// better version = IIR, find average of whole signal then 

float Sound::LPF(float sign, float cf)
{
	float a = (m_lpFilter - 0.4f) + (0.4f * cf);
	float b = 1.0f - a;
	static float z = 0.0f;
	z = (sign * b) + (z * a);
	return z;
}

void Sound::CalculateSignal(const std::string& bufferPath)
{
	memset(m_dataMod, 0, m_totalNumOfFrames);

	for (int i = 0; i != m_totalNumOfFrames - 3; i++)
	{
		float sig1 = m_dataOrig[i];
		float sig2 = m_dataOrig[i + 1];
		float sig3 = m_dataOrig[i + 2];
		float sig4 = cos(m_dataOrig[i + 3] * M_2_PI);

		m_dataMod[i] = LPF((0.3f * sig1) + (0.3f * sig2) + (0.3f * sig3), sig4);
	}

	float max = 0.0f;
	float cur = 0.0f;
	for (int i = 0; i != m_totalNumOfFrames; i++)
	{
		cur = m_dataMod[i];
		cur = abs(cur);
		if (cur > max)
		{
			max = cur;
		}
		m_dataMod[i] *= m_volume;
	}
	
	m_fileInfo.samplerate = m_origFreq * m_frequency;

	if (m_normalised)
	{
		for (int i = 0; i != m_totalNumOfFrames; i++)
		{
			m_dataMod[i] /= max;
		}
	}

	if (m_reversed)
	{
		// reverse signal here
		int sampNum = 0;
		for (int i = m_totalNumOfFrames; i != -1; i--)
		{
			m_dataReversed[sampNum] = m_dataMod[i];
			sampNum++;
		}

		m_fileHandle = sf_open(bufferPath.c_str(), SFM_WRITE, &m_fileInfo);

		if (!m_fileHandle)
		{
			std::cout << "Sound::SetVolume(), Failed to open file" << std::endl;
		}

		sf_write_float(m_fileHandle, m_dataReversed, m_totalNumOfFrames);

		if (sf_close(m_fileHandle) != 0)
		{
			std::cout << "Sound::SetVolume(), Failed to write file" << std::endl;
		}
	}
	else
	{
		m_fileHandle = sf_open(bufferPath.c_str(), SFM_WRITE, &m_fileInfo);

		if (!m_fileHandle)
		{
			std::cout << "Sound::SetVolume(), Failed to open file" << std::endl;
		}

		sf_write_float(m_fileHandle, m_dataMod, m_totalNumOfFrames);

		if (sf_close(m_fileHandle) != 0)
		{
			std::cout << "Sound::SetVolume(), Failed to write file" << std::endl;
		}
	}
}

void Sound::SetVolume(float f)
{
	m_volume = f;
	m_wasChanged = true;

	CalculateSignal("../../Assets/buffer.wav");
}

void Sound::SetFrequency(float f)
{
	m_frequency = f;
	m_wasChanged = true;
	
	CalculateSignal("../../Assets/buffer.wav");
}

// change max/min freq to one bar

void Sound::SetMaxFreq(float f)
{
	m_maxFreq = f;
	m_wasChanged = true;
	
	CalculateSignal("../../Assets/buffer.wav");
}

void Sound::SetMinFreq(float f)
{
	m_minFreq = f;
	m_wasChanged = true;
}

void Sound::SetNormalised(bool b)
{
	m_normalised = b;
	m_wasChanged = true;
	
	CalculateSignal("../../Assets/buffer.wav");
}

void Sound::SetNumOfLoops(int i)
{
	m_numOfLoops = i;
	m_wasChanged = true;
}

void Sound::SetLPFilter(float f)
{
	m_lpFilter = 1.0f - f;
	m_wasChanged = true;
	
	CalculateSignal("../../Assets/buffer.wav");
}

void Sound::SetHPFilter(float f)
{
	m_lpFilter = f;
	m_wasChanged = true;
}

void Sound::SetReversed(bool b)
{
	m_reversed = b;
	m_wasChanged = true;
	
	CalculateSignal("../../Assets/buffer.wav");
}

bool Sound::GetWasChanged() const
{
	return m_wasChanged;
}

void Sound::Play()
{
	if (m_wasChanged)
	{
		Reload();
	}

	Mix_PlayChannel(-1, m_sound, 0);
	m_wasChanged = false;
}